package entity

import (
	"context"
	"fmt"
	"store/config"
	"time"

	"github.com/cihub/seelog"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/utils"
)

var db *gorm.DB

func addSettings() {
	initValue := "db_init"
	entities := []SettingsEntity{
		{
			ID:    &initValue,
			Name:  &initValue,
			Value: &initValue,
		},
	}
	for _, one := range entities {
		create := db.Model(&SettingsEntity{}).FirstOrCreate(&one)
		if nil != create.Error {
			_ = seelog.Error(create.Error)
			panic(create.Error)
		}
	}
}

func needMigration() bool {
	var settings SettingsEntity
	return db.Where("name = ?", "db_init").First(&settings).Error != nil
}

// InitDb 初始化数据库
func InitDb() {
	dbConfig := config.GetConfig().Db
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", dbConfig.Username, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Name)
	open, err := gorm.Open(
		mysql.Open(dsn),
		&gorm.Config{
			Logger:                                   new(GormLogger),
			DisableForeignKeyConstraintWhenMigrating: true,
		})
	if nil != err {
		msg := fmt.Sprintf("数据库连接失败,%s", err.Error())
		_ = seelog.Errorf(msg)
		panic(msg)
	} else {
		dbTmp, err := open.DB()
		if nil != err {
			msg := fmt.Sprintf("数据库设置失败,%s", err.Error())
			_ = seelog.Errorf(msg)
			panic(msg)
		}
		dbTmp.SetMaxIdleConns(dbConfig.MaxIdleConns)
		dbTmp.SetMaxOpenConns(dbConfig.MaxOpenConns)
		dbTmp.SetConnMaxLifetime(time.Second * time.Duration(dbConfig.ConnMaxLifetime))
		db = open
		if needMigration() {
			seelog.Infof("初始化数据库")
			err = open.AutoMigrate(
				&SettingsEntity{},
				&FileEntity{},
			)
			addSettings()
		} else {
			seelog.Infof("无需初始化数据库")
		}
		if nil != err {
			msg := fmt.Sprintf("数据库初始化表格结构失败,%s", err.Error())
			_ = seelog.Errorf(msg)
			panic(msg)
		}
	}
}

// GetDb 获取db连接
func GetDb() *gorm.DB {
	return db
}

// Paginate 分页
func Paginate(page int, size int) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		if page == 0 {
			page = 1
		}
		if size < 1 {
			size = 1
		}
		offset := (page - 1) * size
		return db.Offset(offset).Limit(size)
	}
}

// LikeStr like 字符串
func LikeStr(src string) string {
	return "%" + src + "%"
}

// GormLogger gorm log
type GormLogger struct{}

// LogMode log mode
func (g GormLogger) LogMode(l logger.LogLevel) logger.Interface {
	return g
}

// Info level
func (g GormLogger) Info(c context.Context, msg string, p ...interface{}) {
	seelog.Infof(msg, p)
}

// Warn level
func (g GormLogger) Warn(c context.Context, msg string, p ...interface{}) {
	_ = seelog.Warnf(msg, p)
}

// Error level
func (g GormLogger) Error(c context.Context, msg string, p ...interface{}) {
	_ = seelog.Errorf(msg, p)
}

// Trace level
func (g GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
	elapsed := time.Since(begin)
	sql, rows := fc()
	seelog.Infof("%s %f %d %s", utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
